package com.gonsin.gview.logic.delegate;

import com.gonsin.base.GPageRequest;
import com.gonsin.base.exceptions.MyException;
import com.gonsin.gview.annotations.actions.GSession;
import com.gonsin.gview.annotations.data.GBatch;
import com.gonsin.gview.annotations.interfaces.IBatchActionListener;
import com.gonsin.gview.annotations.model.BatchRequest;
import com.gonsin.gview.logic.GViewEnvironment;
import com.gonsin.gview.logic.exceptions.BaseGViewException;
import com.gonsin.gview.logic.utils.*;
import com.google.gson.reflect.TypeToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.NumberUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.lang.reflect.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 代理，实现确认事件
 */
@Slf4j
public class DelegateBatchActionListenerImpl implements IBatchActionListener {

    /**
     * 调用方法的对象
     */
    private Object delegateService;

    /**
     * 需要调用的方法
     */
    private Method actionMethod;

    private static Type[] PARAMS = {
            HttpServletRequest.class,
            BatchRequest.class,
            new TypeToken<List<String>>(){}.getType(),
            HttpSession.class,
            HttpServletResponse.class,
            String.class,
            new TypeToken<Map<String, Object>>(){}.getType(),
            new TypeToken<List<Integer>>(){}.getType(),
            new TypeToken<List<Double>>(){}.getType(),
    };

    public DelegateBatchActionListenerImpl(Object delegateService, Method actionMethod) {
        // 检查方法是否有效
//        int count = actionMethod.getParameterCount();
//        actionMethod.getParameterTypes();
//        for(int i = 0; i < count; i++){
//            Type p = actionMethod.getGenericParameterTypes()[i];
//            if(!TypeUtils.isSupport(p, PARAMS)){
////                throw new ActionParameterNotSupport(target, actionMethod, p);
//                throw new BaseGViewException(String.format("类%s，方法名%s，参数类型不支持%s", target.getClass().getName(),
//                        actionMethod.getName(), p.getTypeName()));
//            }
//        }
        if (!Modifier.isPublic(actionMethod.getDeclaringClass().getModifiers())) {
            actionMethod.setAccessible(true);
        }
        this.actionMethod = actionMethod;
        this.delegateService = delegateService;

    }

    @Override
    public void onClick(HttpServletRequest request, HttpServletResponse response, BatchRequest batchRequest) throws Throwable {

        Map<String, Object> props = batchRequest.getProps();

        if(CollectionUtils.isEmpty(batchRequest.getKeys())){
            throw new BaseGViewException("批量操作必须至少选择一个");
        }

        int count = actionMethod.getParameterCount();
        Object[] paramsArray = new Object[count];
        for(int i = 0; i < count; i++){
            Class p = actionMethod.getParameterTypes()[i];
            if(HttpSession.class.isAssignableFrom(p)){
                paramsArray[i] = request.getSession();
                continue;
            }

            if(HttpServletResponse.class.isAssignableFrom(p)){
                paramsArray[i] = response;
                continue;
            }

            if(HttpServletRequest.class.isAssignableFrom(p)){
                paramsArray[i] = request;
                continue;
            }

            if(GPageRequest.class.isAssignableFrom(p)){
                GPageRequest pageRequest = new GPageRequest();
                pageRequest.setPage(NumberUtils.parseNumber(Objects.toString(props.get("page")), Integer.class));
                pageRequest.setCount(NumberUtils.parseNumber(Objects.toString(props.get("count")), Integer.class));
                if(props.containsKey("sort")){
                    pageRequest.setSort(Objects.toString(props.get("sort")));
                }
                paramsArray[i] = pageRequest;
                continue;
            }

            // 如果是基本数据类型
            if(ValueUtils.isWrapClass(p)){
                GSession gSession = ValueUtils.getSessionAnnotation(actionMethod, i);
                if(gSession != null){
                    String sessionName = gSession.value();
                    Object value = ValueUtils.parseValue(request.getSession().getAttribute(sessionName), p);
                    paramsArray[i] = value;
                    if(value == null){
                        log.warn("获取到session内【{}】的值为空，请检查session是否书写正确", sessionName);
                    }
                } else {
                    String name = ValueUtils.getParamName(actionMethod, i);
                    Object value = ValueUtils.parseValue(props.get(name), p);
                    paramsArray[i] = value;
                    if(value == null){
//                        log.warn("获取到【{}】的值为空，请查看是否忘记填入@GSession标注或者@GParam标注", name);
                        log.warn("获取到【{}】的值为空，请查看是否在【{}.{}】中忘记填入@GSession标注或者@GParam标注", name, delegateService.getClass().getSimpleName(), actionMethod.getName());
                    }
                }
                continue;
            }

            Type pType = actionMethod.getGenericParameterTypes()[i];
            if(
                    TypeUtils.isAssignable(pType, new TypeToken<List<String>>(){}.getType()) ||
                            TypeUtils.isAssignable(pType, new TypeToken<List<Integer>>(){}.getType()) ||
                            TypeUtils.isAssignable(pType, new TypeToken<List<Double>>(){}.getType())
            ){
                if(CollectionUtils.isEmpty(batchRequest.getKeys())){
                    paramsArray[i] = new ArrayList<>();
                } else {
                    Object value = batchRequest.getKeys().get(0);
                    if(value instanceof Integer && TypeUtils.isAssignable(pType, new TypeToken<List<Integer>>(){}.getType())){
                        paramsArray[i] = batchRequest.getKeys();
                    } else if(value instanceof String && TypeUtils.isAssignable(pType, new TypeToken<List<String>>(){}.getType())){
                        paramsArray[i] = batchRequest.getKeys();
                    } else if(value instanceof Double && TypeUtils.isAssignable(pType, new TypeToken<List<Double>>(){}.getType())){
                        paramsArray[i] = batchRequest.getKeys();
                    } else {
                        paramsArray[i] = null;
                    }
                }
                continue;
            }


            if(
                    TypeUtils.isAssignable(pType, new TypeToken<Set<String>>(){}.getType()) ||
                            TypeUtils.isAssignable(pType, new TypeToken<Set<Integer>>(){}.getType()) ||
                            TypeUtils.isAssignable(pType, new TypeToken<Set<Double>>(){}.getType())
            ){
                if(CollectionUtils.isEmpty(batchRequest.getKeys())){
                    paramsArray[i] = new ArrayList<>();
                } else {
                    Object value = batchRequest.getKeys().get(0);
                    if(value instanceof Integer && TypeUtils.isAssignable(pType, new TypeToken<Set<Integer>>(){}.getType())){
                        Set<Integer> array = new HashSet<>();
                        for(Object v : batchRequest.getKeys()){
                            array.add((Integer) v);
                        }
                        paramsArray[i] = array;

                    } else if(value instanceof String && TypeUtils.isAssignable(pType, new TypeToken<Set<String>>(){}.getType())){
                        Set<String> array = new HashSet<>();
                        for(Object v : batchRequest.getKeys()){
                            array.add((String) v);
                        }
                        paramsArray[i] = array;
                    } else if(value instanceof Double && TypeUtils.isAssignable(pType, new TypeToken<Set<Double>>(){}.getType())){
                        Set<Double> array = new HashSet<>();
                        for(Object v : batchRequest.getKeys()){
                            array.add((Double) v);
                        }
                        paramsArray[i] = array;
                    } else {
                        paramsArray[i] = null;
                    }
                }
                continue;
            }

            // 创建类转存表单
            Object searchPo = null;
            Class searchPoClass = null;
            try {
                searchPoClass = Class.forName(p.getTypeName());
                searchPo = searchPoClass.newInstance();
            } catch (Exception e) {
                log.error(e.toString(), e);
                throw new BaseGViewException(String.format("无法创建类【%s】", p.getName()));
            }
            Map<String, Object> searchPoMap = new HashMap<>();
            Field[] allFields = p.getDeclaredFields();
            for(Field field : allFields){
                try {
                    searchPoMap.put(field.getName(), ValueUtils.parseValue(props.get(field.getName()), field.getGenericType()));
                } catch (Exception ex) {
                    log.warn(ex.toString(), ex);
                    log.warn("设置参数【{}】，设置的内容为“{}“", field.getName(), props.get(field.getName()));
                }
            }
            try {
                BeanUtilsBean utils = BeanUtilsBean.getInstance();
                utils.getConvertUtils().register(new MyDateConverter(), java.util.Date.class);
                utils.getConvertUtils().register(new MyBooleanConverter(), Boolean.class);
                utils.getConvertUtils().register(new MyIntegerConverter(), Integer.class);
                utils.getConvertUtils().register(new MyLongConverter(), Long.class);
                utils.getConvertUtils().register(new MyShortConverter(), Short.class);
                utils.getConvertUtils().register(new MyByteConverter(), Byte.class);
                utils.getConvertUtils().register(new MyDoubleConverter(), Double.class);
                utils.getConvertUtils().register(new MyFloatConverter(), Float.class);
                utils.getConvertUtils().register(new MyCharConverter(), Character.class);
                utils.getConvertUtils().register(new MyListConverter(), List.class);
                utils.getConvertUtils().register(new MySetConverter(), Set.class);
                utils.populate(searchPo, searchPoMap);
                paramsArray[i] = searchPo;

                // 批量字段
                if(CollectionUtils.isEmpty(batchRequest.getKeys())){
                    continue;
                }

                Field batchField = ValueUtils.findAnnotationField(searchPoClass, GBatch.class);
                if(batchField == null){
                    continue;
                }
                ValueUtils.setValue(searchPo, batchField, batchRequest.getKeys());
                continue;
            } catch (Exception e) {
                log.error(e.toString() ,e);
                throw new BaseGViewException(String.format("无法创建类【%s】", p.getTypeName()));
            }
        }

        try {
            actionMethod.invoke(delegateService, paramsArray);
        } catch (Exception e) {
            if(e instanceof InvocationTargetException){
                throw ((InvocationTargetException) e).getTargetException();
            }
            throw e;
        }


//        int count = actionMethod.getParameterCount();
//        Object[] params = new Object[count];
//        Map<String, Object> props = button.getProps();
////        if(!CollectionUtils.isEmpty(button.getProps())){
////            button.getProps().forEach(p -> {
////                props.put(p.getKey(), p.getValue());
////            });
////        }
//        for(int i = 0; i < count; i++){
//
////            Type p = actionMethod.getParameterTypes()[i];
//            Type p = actionMethod.getGenericParameterTypes()[i];
//            int index = ArrayUtils.indexOf(PARAMS, p);
//            Object paramObj = null;
//            switch (index) {
//                case 0: paramObj = request; break;
//                case 1: paramObj = button; break;
//                case 2:         // 参数为List<String>
//                case 7:         // 参数为List<Integer>
//                case 8: {       // 参数为List<Double>
//                    if(CollectionUtils.isEmpty(button.getKeys())){
//                        paramObj = new ArrayList<>();
//                    } else {
//                        Object value = button.getKeys().get(0);
//                        if(value instanceof Integer && index == 7){
//                            paramObj = button.getKeys();
//                        } else if(value instanceof String && index == 2){
//                            paramObj = button.getKeys();
//                        } else if(value instanceof Double && index == 8){
//                            paramObj = button.getKeys();
//                        } else {
//                            paramObj = null;
//                        }
//                    }
////                    if(button.getKeys() != null && TypeUtils.instanceOf(button.getKeys(), p)){
////                        paramObj = button.getKeys();
////                    }
//                } break;
//                case 3: {
//                    paramObj = request.getSession();
//                } break;
//                case 4: {
//                    paramObj = response;
//                } break;
//                case 5:
////                case 7:         // 参数为List<Integer>
////                case 8: {       // 参数为List<Double>
//////                    String key = actionMethod.getParameters()[i].getName();
//////                    paramObj = props.get(key);
////                    if(button.getKeys() != null && TypeUtils.instanceOf(button.getKeys(), p)){
////                        paramObj = button.getKeys();
////                    }
////                } break;
//                case 6: {
//                    paramObj = props;
//                } break;
//                default: {
//                    try {
//                        paramObj = ClassUtils.getClass(p.getTypeName()).newInstance();
//                        BeanUtils.populate(paramObj, props);
//                    } catch (Exception e) {
//                        log.error(e.toString(), e);
//                        throw new BaseGViewException(
//                                String.format("创建类【%s】失败，无法调用方法【%s】", p.getTypeName(), actionMethod.getName()));
//                    }
//                }
//            }
//            params[i] = paramObj;
//        }
//
//        try {
//            actionMethod.invoke(target, params);
//        } catch (Exception e) {
//            log.error(e.toString(), e);
////            throw new ActionRunFailedException(target, actionMethod);
//            throw new BaseGViewException(String.format("类%s的方法%s运行失败", target.getClass().getName(), actionMethod.getName()));
//        }
    }

//    private Field findBatchField(Class searchPoClass) {
//        Field[] allField = searchPoClass.getDeclaredFields();
//        if(ArrayUtils.isEmpty(allField)){
//            return null;
//        }
//        for(Field f : allField){
//            GBatch batch = f.getAnnotation(GBatch.class);
//            if(batch != null){
//                return f;
//            }
//        }
//        return null;
//    }
}
